﻿#include <algorithm>
#include "Array2D.h"
#include "iostream"

using namespace std;

class State {
public:
	State(const char* stageData, int size) {
		setSize(stageData, size);
		mObjects.setSize(mWidth, mHeight);
		mGoalFlags.setSize(mWidth, mHeight);
		for (int y = 0; y < mHeight; ++y) {
			for (int x = 0; x < mWidth; ++x) {
				mObjects(x, y) = OBJ_WALL;
				mGoalFlags(x, y) = false;
			}
		}
		int x = 0;
		int y = 0;
		for (int i = 0; i < size; i++) {
			Object t;
			bool goalFlag = false;
			switch (stageData[i]) {
			case '#':
				t = OBJ_WALL; break;
			case ' ':
				t = OBJ_SPACE; break;
			case 'o':
				t = OBJ_BLOCK; break;
			case 'O':
				t = OBJ_BLOCK; goalFlag = true; break;
			case '.':
				t = OBJ_SPACE; goalFlag = false; break;
			case 'p':
				t = OBJ_MAN; break;
			case 'P':
				t = OBJ_MAN; goalFlag = true; break;
			case '\n':
				x = 0; ++y; t = OBJ_UNKNOWN; break;
			default: t = OBJ_UNKNOWN; break;
			}
			if (t != OBJ_UNKNOWN) {
				mObjects(x, y) = t;
				mGoalFlags(x, y) = goalFlag;
				++x;
			}
		}
	}
	void update(char input) {
		//移动量变换
		int dx = 0;
		int dy = 0;
		switch (input) {
		case 'a': dx = -1; break; //向左
		case 'd': dx = 1; break; //右
		case 'w': dy = -1; break; //上。Y朝下为正
		case 's': dy = 1; break; //下。
		}
		//使用较短的变量名
		int w = mWidth;
		int h = mHeight;
		Array2D< Object >& o = mObjects;
		//查找小人的坐标
		int x, y;
		x = y = -1; //危险值
		bool found = false;
		for (y = 0; y < mHeight; ++y) {
			for (x = 0; x < mWidth; ++x) {
				if (o(x, y) == OBJ_MAN) {
					found = true;
					break;
				}
			}
			if (found) {
				break;
			}
		}
		//鱼洞
		//移动后的坐标
		int tx = x + dx;
		int ty = y + dy;
		//判断坐标的极端值。不允许超出合理值范围
		if (tx < 0 || ty < 0 || tx >= w || ty >= h) {
			return;
		}
		//A.该方向上是空白或者终点。则小人移动
		if (o(tx, ty) == OBJ_SPACE) {
			o(tx, ty) = OBJ_MAN;
			o(x, y) = OBJ_SPACE;
			//B.如果该方向上是箱子。并且该方向的下下个格子是空白或者终点，则允许移动
		}
		else if (o(tx, ty) == OBJ_BLOCK) {
			//检测同方向上的下下个格子是否位于合理值范围
			int tx2 = tx + dx;
			int ty2 = ty + dy;
			if (tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h) { //按键无效
				return;
			}
			if (o(tx2, ty2) == OBJ_SPACE) {
				//按顺序替换
				o(tx2, ty2) = OBJ_BLOCK;
				o(tx, ty) = OBJ_MAN;
				o(x, y) = OBJ_SPACE;
			}
		}
	}
	void draw() const {
		for (int y = 0; y < mHeight; ++y) {
			for (int x = 0; x < mWidth; ++x) {
				Object o = mObjects(x, y);
				bool goalFlag = mGoalFlags(x, y);
				if (goalFlag) {
					switch (o) {
					case OBJ_SPACE: cout << '.'; break;
					case OBJ_WALL: cout << '#'; break;
					case OBJ_BLOCK: cout << 'O'; break;
					case OBJ_MAN: cout << 'P'; break;
					}
				}
				else {
					switch (o) {
					case OBJ_SPACE: cout << ' '; break;
					case OBJ_WALL: cout << '#'; break;
					case OBJ_BLOCK: cout << 'o'; break;
					case OBJ_MAN: cout << 'p'; break;
					}
				}
			}
			cout << endl;
		}
	}
	bool hasCleared() const {
		for (int y = 0; y < mHeight; ++y) {
			for (int x = 0; x < mWidth; ++x) {
				if (mObjects(x, y) == OBJ_BLOCK) {
					if (mGoalFlags(x, y) == false) {
						return false;
					}
				}
			}
		}
		return true;
	}
private:
	enum Object {
		OBJ_SPACE,
		OBJ_WALL,
		OBJ_BLOCK,
		OBJ_MAN,
		OBJ_UNKNOWN,
	};
	void setSize(const char* stageData, int size) {
		mWidth = mHeight = 0;
		int x = 0;
		int y = 0;
		for (int i = 0; i < size; ++i) {
			switch (stageData[i]) {
			case '#':
			case ' ':
			case 'o':
			case 'O':
			case '.':
			case 'p':
			case 'P':
				++x;
				break;
			case '\n':
				++y;
				mWidth = std::max(mWidth, x);
				mHeight = std::max(mHeight, y);
				x = 0;
				break;
			}
		}
	}
	int mWidth;
	int mHeight;
	Array2D<Object> mObjects;
	Array2D<bool> mGoalFlags;
};